import { Scene } from "@babylonjs/core/scene";
import { CanvasTextureBase, ICanvasTextureOptions } from "./canvas-texture-base";
import { Utils } from "../tool/utils";


const PI = Math.PI;
const HPI = Math.PI * 0.5;

/**
 * 文本画布纹理参数对象
 * @param text 文字内容
 * @param background 背景颜色
 * @param border 边框颜色
 * @param fontWeight 字体权重
 * @param fontStyle 字体样式
 * @param fontSize 字体大小
 * @param fontFamily 字体
 * @param filterSpace 滤除空格
 * @param rowLength 单行最多字数
 * @param marginHorizontal 水平边距
 * @param marginVertical 垂直边距
 * @param offsetRatioVertical 垂直偏移
 * @param lineHeightRatio 行高
 * @param pixelRatio 像素比
 * @param vivid 清晰度
 * @param lineWidth 线条宽
 * @param borderRadius 倒角半径
 * @param borderRadiusLeftBottom 左下倒角半径
 * @param borderRadiusLeftTop 左上倒角半径
 * @param borderRadiusRightTop 右上倒角半径
 * @param borderRadiusRightBottom 右下倒角半径
 * @param fontColor 字体颜色
 * @param alignCenter 是否居中
 */
interface ICanvasTextureTextOptions extends ICanvasTextureOptions {
    text?: string,
    background?: string,
    border?: string,
    fontWeight?: string,
    fontStyle?: string,
    fontSize?: number,
    fontFamily?: string,
    filterSpace?: boolean,
    rowLength?: number,
    marginHorizontal?: number,
    marginVertical?: number,
    offsetRatioVertical?: number,
    lineHeightRatio?: number,
    pixelRatio?: number,
    vivid?: number,
    lineWidth?: number,
    borderRadius?: number,
    borderRadiusLeftBottom?: number,
    borderRadiusLeftTop?: number,
    borderRadiusRightTop?: number,
    borderRadiusRightBottom?: number,
    fontColor?: string,
    alignCenter?: boolean,
}


/**
 * 文本画布纹理
 * @param name 名称
 * @param options 参数对象
 * @param scene 所在场景
 */
class CanvasTextureText extends CanvasTextureBase {

    private _tA1!: string[];
    private _tA2!: number[];
    private _tA3!: string[];
    private _tA0!: number[];
    private _width!: number;
    private _height!: number;

    constructor(name: string, options?: ICanvasTextureTextOptions, scene?: Scene) {
        super(name, options, scene);
    }

    /**
     * 获取纹理宽（缩放x值）
     */
    public get width(): number {
        return this._width;
    }

    /**
     * 获取纹理高（缩放y值）
     */
    public get height(): number {
        return this._height;
    }

    protected initialzeVariable(): void {
        this._tA0 = [];
        this._tA1 = [];
        this._tA2 = [];
        this._tA3 = [];
    }

    /**
     * 检测移除多余的空格
     * @param ary 字符集合
     */
    private checkEndSpace(ary: string[]): void {
        if (ary.length > 0 && ary[0] === ' ') ary.splice(0, 1);
        if (ary.length > 0 && ary[ary.length - 1] === ' ') ary.splice(ary.length - 1, 1);
    }

    /**
     * 分行
     * @param _text 文本内容 
     * @param options 参数对象
     * @param out 输出字符集合
     */
    private splitRow(_text: string, options: ICanvasTextureTextOptions, out: string[]): void {
        let text: string = _text;
        if (options.filterSpace) text = Utils.filterSpace(_text);
        out.length = 0;
        const ws = this._tA0;
        ws.length = 0;
        ws[0] = 0;
        let rw = 0;
        let rl = options.rowLength || 128;

        this._tA3.length = 0;

        for (let a = 0; a < text.length; a++) {
            rw = text.charCodeAt(a) > 255 ? 1 : 0.5;
            if (rw + ws[ws.length - 1] > rl) {

                if (options.filterSpace) this.checkEndSpace(this._tA3);
                out.push(this._tA3.join(''));

                ws.push(0);
                this._tA3.length = 0;
            }
            this._tA3.push(text[a]);
            ws[ws.length - 1] += rw;
        }
        if (options.filterSpace) this.checkEndSpace(this._tA3);
        out.push(this._tA3.join(''));
    }
    protected drawCanvas(options: ICanvasTextureTextOptions): HTMLCanvasElement {
        const row = this._tA1;
        const text = options.text || 'default';
        const targetCanvas = options.canvas;
        this.splitRow(text, options, row);
        let mh = options.marginHorizontal || 20;
        let mv = options.marginVertical || 20;
        let ov = options.offsetRatioVertical || 0.35;
        let lineRatio = options.lineHeightRatio || 1.2;
        const fontSize = options.fontSize || 12;
        const vivid = options.vivid || 2;
        let _fontSize = fontSize * (options.pixelRatio || 1) * vivid;
        let ratBasic = 100 * 16 / fontSize * vivid;

        const canvas = targetCanvas || document.createElement('canvas');
        const ctx = canvas.getContext('2d')!;
        const fontWeight = options.fontWeight || 'normal';
        const fontStyle = options.fontStyle || 'normal';
        const fontFamily = options.fontFamily || 'Arial';
        ctx.font = '' + fontWeight + ' ' + fontStyle + ' ' + _fontSize + 'px ' + fontFamily + '';
        let maxW = Number.NEGATIVE_INFINITY;
        this._tA2.length = 0;
        for (let ro of row) {
            const cw = ctx.measureText(ro).width + mh * 2;
            this._tA2.push(cw);
            if (maxW < cw) {
                maxW = cw;
            }
        }
        canvas.width = maxW;
        canvas.height = _fontSize * row.length * lineRatio + mv * 2;
        const ratX = canvas.width / ratBasic;
        const ratY = canvas.height / canvas.width * ratX;
        const _lineWidth = options.lineWidth || 0.04;
        let lineWidth = _lineWidth < 1 ? canvas.height * _lineWidth : _lineWidth;

        if (options.background && !options.border) {
            ctx.rect(0, 0, canvas.width, canvas.height);
            ctx.fillStyle = options.background;
            ctx.fill();
        }
        if (options.border) {
            ctx.lineWidth = lineWidth;
            const w = lineWidth;

            const width = canvas.width, height = canvas.height;
            let r0 = options.borderRadiusLeftBottom || 0;
            let r1 = options.borderRadiusLeftTop || 0;
            let r2 = options.borderRadiusRightTop || 0;
            let r3 = options.borderRadiusRightBottom || 0;
            if (options.borderRadius !== undefined) {
                r0 = r1 = r2 = r3 = options.borderRadius;
            }

            ctx.arc(w + r0, height - w - r0, r0, HPI, PI, false);
            ctx.arc(w + r1, w + r1, r1, PI, -HPI, false);
            ctx.arc(width - w - r2, w + r2, r2, -HPI, 0, false);
            ctx.arc(width - w - r3, height - w - r3, r3, 0, HPI, false);
            ctx.lineTo(w + r0, height - w);
            ctx.fillStyle = options.background || '#ffffff';
            ctx.fill();
            ctx.strokeStyle = options.border;
            ctx.stroke();
        }
        ctx.font = '' + fontWeight + ' ' + fontStyle + ' ' + _fontSize + 'px ' + fontFamily + '';
        const color = options.fontColor || '#ffffff';
        ctx.fillStyle = color;

        for (let b = 0; b < row.length; b++) {
            let sx = options.alignCenter ? mh + (maxW - this._tA2[b]) * 0.5 : mh;
            ctx.fillText(row[b], sx, (b + 0.5) * _fontSize * lineRatio + _fontSize * ov + mv);
        }
        this._width = ratX;
        this._height = ratY;
        return canvas;

    }

}


export { CanvasTextureText };
export type { ICanvasTextureTextOptions };